package engine.util.draw;

import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;

import engine.gameObject.model.Model;
import engine.material.DefaultMaterial;

public class Disk extends Model{

	public Disk(float innerRadius, float outerRadius, int slices, int loops) {
		super(getIndexPointer(innerRadius, outerRadius, slices, loops), getVAOID(innerRadius, outerRadius, slices, loops), new DefaultMaterial(), 0, true, getTriangleCount(
				slices, loops));
		
		setBoundingRadius(outerRadius);
	}
	
	public static int getIndexPointer(float innerRadius, float outerRadius, int slices, int loops) {
		IntBuffer indexBuffer = BufferUtils.createIntBuffer(getTriangleCount(
				slices, loops));
		
		for(int i = 0; i < loops; i++) {
			int baseIndexTop = i * (slices + 1);
			int baseIndexBottom = (i + 1) * (slices + 1);

			for (int j = 0; j < slices; j++) {
				// first triangle
				indexBuffer.put(j + baseIndexBottom);
				indexBuffer.put((j + 1) + baseIndexBottom);
				indexBuffer.put(j + baseIndexTop);

				// second triangle
				indexBuffer.put(j + baseIndexTop);
				indexBuffer.put((j + 1) + baseIndexBottom);
				indexBuffer.put((j + 1) + baseIndexTop);
			}
		}
		
		indexBuffer.rewind();

		int indexPointer = glGenBuffers();
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexPointer);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBuffer, GL_STATIC_DRAW);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

		return indexPointer;
	}
	
	public static int getVAOID(float innerRadius, float outerRadius, int slices, int loops) {
		int numVertices = (slices + 1) * (loops + 1);
		
		FloatBuffer buffer = BufferUtils.createFloatBuffer(numVertices * 8);
		
		for(int i = 0; i <= loops; i++) {
			float radius = innerRadius + i * (outerRadius - innerRadius) / loops;
			
			for(int j = 0; j <= slices; j++) {
				float angle = (float)Math.PI * 2f * j / slices;
				
				float x = (float)Math.cos(angle) * radius;
				float z = (float)Math.sin(angle) * radius;
				
				buffer.put(x);
				buffer.put(0);
				buffer.put(z);
				
				buffer.put(x / outerRadius / 2 + 0.5f);
				buffer.put(z / outerRadius / 2 + 0.5f);
				
				buffer.put(0);
				buffer.put(1);
				buffer.put(0);
			}
		}
		
		//make vao out of vertices, normals and texcoords. 
		buffer.rewind();
		
		int stride = (3 + 2 + 3) * 4;

		int vaoID = glGenVertexArrays();
		glBindVertexArray(vaoID);

		int vboID = glGenBuffers();
		glBindBuffer(GL_ARRAY_BUFFER, vboID);
		glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);

		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 3, GL_FLOAT, false, stride, 0);
		
		glEnableVertexAttribArray(1);
		glVertexAttribPointer(1, 2, GL_FLOAT, false, stride, 12);
		
		glEnableVertexAttribArray(2);
		glVertexAttribPointer(2, 3, GL_FLOAT, false, stride, 20);

		glBindBuffer(GL_ARRAY_BUFFER, 0);

		glBindVertexArray(0);

		return vaoID;
	}
	
	public static int getTriangleCount(int slices, int loops) {
		return slices * 2 * loops * 3;
	}
}
